// js 开发中，一般用事件模型替代传统的发布-订阅模式

/**
 * 支持先发布后订阅，即离线模式
 */
class MyEvent {
    constructor(name) {
        this.name = name || 'default';
        this._cache = {};
        this._offlineCache = {}; // 离线事件
    }

    listen(key, fn) {
        if (!this._cache[key]) {
            this._cache[key] = [];
        }
        this._cache[key].push(fn);

        if (this._offlineCache[key]) {
            this._offlineCache[key](); // 触发离线事件
            this._offlineCache[key] = null;
        }
    }

    one(key, fn) {
        this.remove(key);
        this.listen(key, fn);
    }

    trigger(...args) {
        let key = args.shift();
        let fns = this._cache[key];

        if (!fns || fns.length===0) {
            this._offlineCache[key] = () => this.trigger(key, ...args);
            return;
        }

        for (let fn of fns) {
            fn(...args);
        }
    }

    remove(key, fn) {
        let fns = this._cache[key];
        if (fns) {
            if (fn) {
                for (let i = fns.length-1; i >= 0; i--) { // 反向遍历订阅的回调函数列表
                    if (fns[i] === fn) {
                        fns.splice(i, 1);
                    }
                }
            } else {
                this._cache[key] = [];
            }
        }
    }
}

// test
let myEvent = new MyEvent();

myEvent.trigger('click', new Date().toLocaleString());
myEvent.trigger('click', new Date().toLocaleString()); // 离线事件仅最后一次有效
myEvent.listen('click', (datetime) => {
    console.log(`[on click] ${datetime}`);
});

myEvent.one('test-one', (num) => {
    console.log(`[on test-one] ${num}`);
});
myEvent.one('test-one', (num) => {
    console.log(`[on test-one] ${num + 1}`);
});
myEvent.trigger('test-one', 1);

let loginEvent = new MyEvent('login');
loginEvent.listen('success', (data) => {
    console.log(`[${loginEvent.name}][on success] ${data}`);
});
loginEvent.trigger('success', JSON.stringify({name:'leo',age:23}));
loginEvent.trigger('success', JSON.stringify({name:'jack',age:25}));
